package com.googlecode.battleofendor.jme;

import jmetest.renderer.TestBoxColor;

import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingSphere;
import com.jme.image.Texture;
import com.jme.input.KeyInput;
import com.jme.input.action.InputActionEvent;
import com.jme.input.action.KeyInputAction;
import com.jme.math.Vector3f;
import com.jme.renderer.Renderer;
import com.jme.scene.Controller;
import com.jme.scene.TriMesh;
import com.jme.scene.Spatial.CullHint;
import com.jme.scene.Spatial.LightCombineMode;
import com.jme.scene.shape.Sphere;
import com.jme.scene.state.BlendState;
import com.jme.scene.state.CullState;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.ZBufferState;
import com.jme.util.TextureManager;
import com.jme.util.Timer;
import com.jmex.effects.TrailMesh;

public class TestLasers extends SimpleGame 
{
	private TrailMesh theTrailMesh;
	private Vector3f tangent = new Vector3f();
	
    protected void simpleInitGame() 
    {
        display.setTitle("LaserTest");

        cam.getLocation().set(new Vector3f(140, 140, 140));
        cam.lookAt(new Vector3f(), Vector3f.UNIT_Y);

        // Create the trail
        theTrailMesh = new TrailMesh("TrailMesh", 100);
        theTrailMesh.setUpdateSpeed(60.0f);
        theTrailMesh.setFacingMode(TrailMesh.FacingMode.Billboard);
        theTrailMesh.setUpdateMode(TrailMesh.UpdateMode.Step);

        // Try out some additive blending etc
        theTrailMesh.setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);
        theTrailMesh.setCullHint(CullHint.Never);

        TextureState ts = display.getRenderer().createTextureState();
        ts.setEnabled(true);
        
        Texture t1 = TextureManager.loadTexture(
                TestBoxColor.class.getClassLoader().getResource(
                        "jmetest/data/texture/trail.png"),
                Texture.MinificationFilter.Trilinear,
                Texture.MagnificationFilter.Bilinear);
        
        ts.setTexture(t1);
        theTrailMesh.setRenderState(ts);

        BlendState bs = display.getRenderer().createBlendState();
        bs.setBlendEnabled(true);
        bs.setSourceFunction(BlendState.SourceFunction.SourceAlpha);
        bs.setDestinationFunction(BlendState.DestinationFunction.One);
        bs.setTestEnabled(true);
        theTrailMesh.setRenderState(bs);

        ZBufferState zs = display.getRenderer().createZBufferState();
        zs.setWritable(false);
        theTrailMesh.setRenderState(zs);

        CullState cs = display.getRenderer().createCullState();
        cs.setCullFace(CullState.Face.None);
        cs.setEnabled(true);
        theTrailMesh.setRenderState(cs);

        rootNode.attachChild(theTrailMesh);
        
        input.addAction(new FireLaser(), "firelaser", KeyInput.KEY_F, false);

        // No lighting for clarity
        rootNode.setLightCombineMode(LightCombineMode.Off);
        rootNode.setRenderQueueMode(com.jme.renderer.Renderer.QUEUE_OPAQUE);
    }

    public static void main(String[] args) {
    	TestLasers app = new TestLasers();
        app.setConfigShowMode(ConfigShowMode.AlwaysShow);
        app.start();
    }
    
    class FireLaser extends KeyInputAction {
        int numBullets;

        public void performAction(InputActionEvent evt) {
        	
        	TrailMesh newLaser = new TrailMesh("newLaser" + numBullets++, 5);
            newLaser.setUpdateSpeed(60.0f);
            newLaser.setFacingMode(TrailMesh.FacingMode.Billboard);
            newLaser.setUpdateMode(TrailMesh.UpdateMode.Step);

            // Try out some additive blending etc
            newLaser.setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);
            newLaser.setCullHint(CullHint.Never);

            /**
             * The question is, why isn't the commented code below needed to 
             * keep the lasers colored? (i.e. Not white) 
             */
//            TextureState ts = display.getRenderer().createTextureState();
//            ts.setEnabled(true);
//            Texture t1 = TextureManager.loadTexture(
//                    TestBoxColor.class.getClassLoader().getResource(
//                            "jmetest/data/texture/trail.png"),
//                    Texture.MinificationFilter.Trilinear,
//                    Texture.MagnificationFilter.Bilinear);
//            ts.setTexture(t1);
//            trailMesh.setRenderState(ts);
//
//            BlendState bs = display.getRenderer().createBlendState();
//            bs.setBlendEnabled(true);
//            bs.setSourceFunction(BlendState.SourceFunction.SourceAlpha);
//            bs.setDestinationFunction(BlendState.DestinationFunction.One);
//            bs.setTestEnabled(true);
//            trailMesh.setRenderState(bs);
//
//            ZBufferState zs = display.getRenderer().createZBufferState();
//            zs.setWritable(false);
//            trailMesh.setRenderState(zs);
//
//            CullState cs = display.getRenderer().createCullState();
//            cs.setCullFace(CullState.Face.None);
//            cs.setEnabled(true);
//            trailMesh.setRenderState(cs);

            rootNode.attachChild(newLaser);

            Sphere bullet = new Sphere("bullet" + numBullets++, 8, 8, 0.001f);
            bullet.setModelBound(new BoundingSphere());
            bullet.updateModelBound();
            /** Move bullet to the camera location */
            bullet.setLocalTranslation(new Vector3f(15,1,1));

            /**
             * Update the new world location for the bullet before I add a
             * controller
             */
            bullet.updateGeometricState(0, true);
            /**
             * Add a movement controller to the bullet going in the camera's
             * direction
             */
            bullet.addController(new LaserMover(bullet, newLaser, new Vector3f(new Vector3f(1,1,1))));
            rootNode.attachChild(bullet);
            bullet.updateRenderState();
        }
    }

    class LaserMover extends Controller 
    {
        private static final long serialVersionUID = 1L;
        /** Bullet that's moving */
        TriMesh bullet;
        
        TrailMesh laser;

        /** Direction of bullet **/
        Vector3f direction;

        /** speed of bullet */
        float speed = 1000;

        /** Seconds it will last before going away */
        float lifeTime = 10;

        LaserMover(TriMesh bullet, TrailMesh trailMesh, Vector3f direction) {
            this.bullet = bullet;
            this.laser = trailMesh;
            this.direction = direction;
            this.direction.normalizeLocal();
        }

        public void update(float time) 
        {
            lifeTime -= time;
        
            /** If life is gone, remove it **/
            if (lifeTime < 0) 
            {
            	rootNode.detachChild(laser);
                rootNode.detachChild(bullet);
                bullet.removeController(this);
                laser.removeController(this);
                return;
            }
            
            /** Move bullet */
            Vector3f bulletPos = bullet.getLocalTranslation();
            bulletPos.addLocal(direction.mult(time * speed));
            bullet.setLocalTranslation(bulletPos);

            laser.setLocalTranslation(direction.mult(time * speed));
            laser.updateGeometricState(0.0f, true);
     
            Vector3f tg = new Vector3f();
            
            // Create a spin for tangent mode
            tg.set(direction.mult(time * speed));
            tg.normalizeLocal();

            // Setup width
            float width = 1.0f;
            
            laser.setTrailFront(bullet.getWorldTranslation(), tangent,
                    width, Timer.getTimer().getTimePerFrame());

            laser.update(cam.getLocation());
        }
    }
}